﻿// //   Copyright 2007-2011 Comdiv (F. Sadykov) - http://code.google.com/u/fagim.sadykov/
// //   Supported by Media Technology LTD 
// //    
// //   Licensed under the Apache License, Version 2.0 (the "License");
// //   you may not use this file except in compliance with the License.
// //   You may obtain a copy of the License at
// //    
// //        http://www.apache.org/licenses/LICENSE-2.0
// //    
// //   Unless required by applicable law or agreed to in writing, software
// //   distributed under the License is distributed on an "AS IS" BASIS,
// //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// //   See the License for the specific language governing permissions and
// //   limitations under the License.
// //   
// //   MODIFICATIONS HAVE BEEN MADE TO THIS FILE

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Comdiv.QWeb.Logging;

namespace Comdiv.QWeb.Files {
	public static class FileNameResolverExtensions {
		public static readonly string[] DEFAULT_VIEW_RESOLVE_PROBE_PATHS = new[]
		                                                                   	{
		                                                                   		"usr/views", "mod/views", "extensions",
		                                                                   		"sys/views", "views", "tmp/_internal",
		                                                                   	};

		public static readonly string[] DEFAULT_QVIEW_RESOLVE_PROBE_PATHS = new[]
		                                                                    	{
		                                                                    		"usr/qviews", "mod/qviews", "extensions",
		                                                                    		"sys/qviews", "locale/qviews", "qviews",
		                                                                    		"tmp/_internal"
		                                                                    	};

		public static readonly string[] DEFAULT_SCRIPT_RESOLVE_PROBE_PATHS = new[]
		                                                                     	{
		                                                                     		"usr/scripts", "usr", "mod/scripts", "mod",
		                                                                     		"extensions", "sys/scripts", "sys", "scripts",
		                                                                     		"tmp/_internal",
		                                                                     		""
		                                                                     	};

		public static readonly string[] DEFAULT_CSS_RESOLVE_PROBE_PATHS = new[]
		                                                                  	{
		                                                                  		"usr/styles", "usr", "mod/styles", "mod",
		                                                                  		"extensions", "sys/styles", "sys", "styles", "",
		                                                                  		"tmp/_internal"
		                                                                  	};

		public static readonly string[] DEFAULT_IMAGE_RESOLVE_PROBE_PATHS = new[]
		                                                                    	{
		                                                                    		"usr/images", "usr", "mod/images", "mod",
		                                                                    		"extensions", "sys/images", "sys", "images",
		                                                                    		"tmp/_internal"
		                                                                    	};

		public static readonly string[] DEFAULT_CONFIG_RESOLVE_PROBE_PATHS = new[]
		                                                                     	{
		                                                                     		"usr/config", "usr", "mod/config", "mod",
		                                                                     		"extensions", "sys/config", "sys", "config", ""
		                                                                     		, "tmp/_internal"
		                                                                     	};

		public static readonly string[] DEFAULT_USRFILE_RESOLVE_PROBE_PATHS = new[]
		                                                                      	{"usr", "mod", "extensions", "sys", "", "tmp"};

		public static readonly string[] DEFAULT_BIN_RESOLVE_PROBE_PATHS = new[] {"bin", "tmp/dlls", "tmp"};
		private static readonly object writelock = new object();

		public static string[] GetDefaultProbesPaths(string extension) {
			//unification of extensions
			extension = extension.ToUpper();
			if (extension.StartsWith(".")) extension = extension.Substring(1);
			switch (extension) {
				case "JS":
					return DEFAULT_SCRIPT_RESOLVE_PROBE_PATHS;
				case "CSS":
					return DEFAULT_CSS_RESOLVE_PROBE_PATHS;
				case "BRAIL":
					return DEFAULT_VIEW_RESOLVE_PROBE_PATHS;

				case "PNG":
					return DEFAULT_IMAGE_RESOLVE_PROBE_PATHS;
				case "JPG":
					return DEFAULT_IMAGE_RESOLVE_PROBE_PATHS;
				case "JPEG":
					return DEFAULT_IMAGE_RESOLVE_PROBE_PATHS;
				case "GIF":
					return DEFAULT_IMAGE_RESOLVE_PROBE_PATHS;
				case "TIF":
					return DEFAULT_IMAGE_RESOLVE_PROBE_PATHS;
				case "TIFF":
					return DEFAULT_IMAGE_RESOLVE_PROBE_PATHS;

				case "CONFIG":
					return DEFAULT_CONFIG_RESOLVE_PROBE_PATHS;

				case "DLL":
					return DEFAULT_BIN_RESOLVE_PROBE_PATHS;
				case "EXE":
					return DEFAULT_BIN_RESOLVE_PROBE_PATHS;
				default:
					return DEFAULT_USRFILE_RESOLVE_PROBE_PATHS;
			}
		}

		//overload for single file resolving to EXISTED FILE AND DEFAULT PROBES on current QWebContext by default
		public static string Resolve(this IFileNameResolver resolver, string name, bool existedOnly = true,
		                             string[] probePaths = null, QWebContext context = null, ILogListener log = null) {
			context = context ?? QWebContext.Current;
			return resolver.Resolve(context, FileResolveResultType.FullPath, existedOnly, probePaths, new[] {name}, log);
		}

		//overload for multiple file resolving to EXISTED FILE AND DEFAULT PROBES on current QWebContext by default
		public static string Resolve(this IFileNameResolver resolver, string[] names, bool existedOnly = true,
		                             string[] probePaths = null, QWebContext context = null, ILogListener log = null) {
			context = context ?? QWebContext.Current;
			return resolver.Resolve(context, FileResolveResultType.FullPath, existedOnly, probePaths, names, log);
		}

		//overload for single file resolving to EXISTED LOCAL FILE AND DEFAULT PROBES on current QWebContext by default
		public static string ResolveLocal(this IFileNameResolver resolver, string name, bool existedOnly = true,
		                                  string[] probePaths = null, QWebContext context = null, ILogListener log = null) {
			context = context ?? QWebContext.Current;
			return resolver.Resolve(context, FileResolveResultType.LocalPath, existedOnly, probePaths, new[] {name}, log);
		}

		//overload for multiple file resolving to EXISTED LOCAL FILE AND DEFAULT PROBES on current QWebContext by default
		public static string ResolveLocal(this IFileNameResolver resolver, string[] names, bool existedOnly = true,
		                                  string[] probePaths = null, QWebContext context = null, ILogListener log = null) {
			context = context ?? QWebContext.Current;
			return resolver.Resolve(context, FileResolveResultType.LocalPath, existedOnly, probePaths, names, log);
		}

		//overload for single file resolving to EXISTED FILE URL AND DEFAULT PROBES on current QWebContext by default
		public static string ResolveUrl(this IFileNameResolver resolver, string name, bool existedOnly = true,
		                                string[] probePaths = null, QWebContext context = null, ILogListener log = null) {
			context = context ?? QWebContext.Current;
			return resolver.Resolve(context, FileResolveResultType.LocalUrl, existedOnly, probePaths, new[] {name}, log);
		}

		//overload for multiple file resolving to EXISTED FILE URL AND DEFAULT PROBES on current QWebContext by default
		public static string ResolveUrl(this IFileNameResolver resolver, string[] names, bool existedOnly = true,
		                                string[] probePaths = null, QWebContext context = null, ILogListener log = null) {
			context = context ?? QWebContext.Current;
			return resolver.Resolve(context, FileResolveResultType.LocalUrl, existedOnly, probePaths, names, log);
		}

		public static string NormalizePath(this string path) {
			if (null == path) return null;
			return path.Replace("\\", "/").Replace("//", "/").Replace("//", "/").ToLower();
		}


		public static void Externalize(this IFileNameResolver resolver, string mask, Assembly assembly = null,
		                               string outdir = "~/tmp/_internal",
		                               string cleanupnamepattern = @"^(?i)[\s\S]+?\.resources\.", ILogListener log = null) {
			lock (writelock) {
				log = log ?? (resolver.Registry == null ? StubLogListener.Default : resolver.Registry.Log);
				if (string.IsNullOrWhiteSpace(mask)) mask = ".";
				assembly = assembly ?? Assembly.GetCallingAssembly();
				outdir = outdir ?? "~/tmp/_internal";
				cleanupnamepattern = cleanupnamepattern ?? @"^(?i)[\s\S]+?\.resources\.";
				var maskregex = new Regex(mask, RegexOptions.Compiled);
				var cleanupregex = new Regex(cleanupnamepattern, RegexOptions.Compiled);
				var resourcenames = assembly.GetManifestResourceNames().Where(x => maskregex.IsMatch(x)).ToArray();
				foreach (var resourcename in resourcenames) {
					using (var sr = new StreamReader(assembly.GetManifestResourceStream(resourcename))) {
						var name = cleanupregex.Replace(resourcename, "").Replace("___", ".").Replace("__", "/");
							//psuedo folder support for embeded resources and avoid for limitation of CS embeded resources
						var path = outdir + "/" + name;
						var fullpath = resolver.Resolve(path, false, log: log);
						var dirpath = Path.GetDirectoryName(fullpath);
						Directory.CreateDirectory(dirpath);
						var content = sr.ReadToEnd();
						File.WriteAllText(fullpath, content);
					}
				}
			}
		}

		public static IFileNameResolver Write(this IFileNameResolver r, string name, object content,
		                                      QWebContext context = null, ILogListener log = null, bool append = false) {
			lock (writelock) {
				log = log ?? (r.Registry == null ? StubLogListener.Default : r.Registry.Log);
				log.debug("start write " + name, context);
				var path = r.Resolve(name, false, null, context ?? QWebContext.Current, log);
				log.debug("path resolved as " + path, context);
				Directory.CreateDirectory(Path.GetDirectoryName(path));
				if (append) {
					appendFile(path, content);
				}
				else {
					rewriteFile(path, content);
				}
				r.ClearCache();
				log.trace("file saved " + path, context);
				return r;
			}
		}

		private static void rewriteFile(string path, object content) {
			if (content == null) content = "";
			if (content is string) {
				File.WriteAllText(path, (string) content);
			}
			else if (content is byte[]) {
				File.WriteAllBytes(path, (byte[]) content);
			}
			else {
				File.WriteAllText(path, content.ToString());
			}
		}

		private static void appendFile(string path, object content) {
			if (content == null) content = "";
			if (content is string) {
				File.AppendAllText(path, (string) content);
			}
			else if (content is byte[]) {
				throw new NotImplementedException();
			}
			else {
				File.AppendAllText(path, content.ToString());
			}
		}

		public static T Read<T>(this IFileNameResolver r, string name, QWebContext context = null, ILogListener log = null) {
			if (typeof (T) == typeof (string) || typeof (T) == typeof (XElement) || typeof (T) == typeof (byte[])) {
				log = log ?? (r.Registry == null ? StubLogListener.Default : r.Registry.Log);
				log.debug("start write " + name, context);
				var path = r.Resolve(name, true, null, context ?? QWebContext.Current, log);

				if (null == path) {
					log.error("file not found", context);
					throw new FileNotFoundException(name);
				}
				log.debug("path resolved as " + path, context);
				object result = default(T);
				if (typeof (T) == typeof (string)) {
					result = File.ReadAllText(path);
				}
				else if (typeof (T) == typeof (byte)) {
					result = File.ReadAllBytes(path);
				}
				else if (typeof (T) == typeof (XElement)) {
					result = XElement.Load(path);
				}
				log.trace("file readed " + path, context);
				return (T) result;
			}
			else {
				throw new ArgumentException("Read method supports only strings, XElement and byte[] as result");
			}
		}
	}
}